//==================================================================================================
/**
  EVE - Expressive Vector Engine
  Copyright : EVE Project Contributors
  SPDX-License-Identifier: BSL-1.0
**/
//==================================================================================================
#include "test.hpp"

#include <eve/module/core.hpp>
#include <eve/module/special.hpp>

#include <cmath>

//==================================================================================================
// Types tests
//==================================================================================================
TTS_CASE_TPL("Check return types of erfcx", eve::test::simd::ieee_reals)
<typename T>(tts::type<T>)
{
  using v_t = eve::element_type_t<T>;

  TTS_EXPR_IS(eve::erfcx(T()), T);
  TTS_EXPR_IS(eve::erfcx(v_t()), v_t);
};

//==================================================================================================
// erfcx  tests
//==================================================================================================
TTS_CASE_WITH("Check behavior of erfcx on wide",
              eve::test::simd::ieee_reals,
              tts::generate(tts::randoms(-5.0, 5.0)))
<typename T>(T const& a0)
{
  using v_t = eve::element_type_t<T>;
  using eve::as;
  using eve::erfcx;
  TTS_ULP_EQUAL(
      erfcx(a0), tts::map([&](auto e) -> v_t { return std::exp(e * e) * std::erfc(e); }, a0), 8);
  TTS_ULP_EQUAL(erfcx(T(-0.0)), T(1), 0);
  TTS_ULP_EQUAL(erfcx(T(0)), T(1), 0);
  TTS_ULP_EQUAL(erfcx(T(eve::epso_2(as<T>()))), T(0.999999999999999874724746818321), 1.0);
  TTS_ULP_EQUAL(erfcx(T(0.25)), T(0.7703465477309967439167391723367911261876423850266), 1.0);
  TTS_ULP_EQUAL(erfcx(T(0.5)), T(0.61569034419292587487079342268374193678230639126563), 1.0);

  TTS_ULP_EQUAL(erfcx(T(1)), T(0.42758357615580700441075034449051518082015950316425), 1.0);
  TTS_ULP_EQUAL(erfcx(T(2)), T(0.25539567631050574386508858090854276330259930525524), 1.0);
  TTS_ULP_EQUAL(erfcx(T(4)), T(0.1369994576250613898894451714), 1.0);

  TTS_ULP_EQUAL(erfcx(T(5)), T(0.110704637733068626370212086492), 1.0);
  TTS_ULP_EQUAL(erfcx(T(27)), T(0.0208816079904209406740944901929), 1.0);
  TTS_ULP_EQUAL(erfcx(T(100)), T(0.00564161378298943290355645700695), 1.0);
  if constexpr(eve::platform::supports_denormals)
  {
    auto asympt = [](auto x) { return eve::rsqrt(eve::pi(as<T>())) / x; };

    TTS_ULP_EQUAL(erfcx(eve::valmax(as<T>())), asympt(eve::valmax(as<T>())), 1.0);
    TTS_ULP_EQUAL(
      erfcx(eve::prev(eve::valmax(as<T>()), 3)), asympt(eve::prev(eve::valmax(as<T>()), 3)), 1.0);
    TTS_ULP_EQUAL(
      erfcx(eve::prev(eve::valmax(as<T>()), 2)), asympt(eve::prev(eve::valmax(as<T>()), 2)), 1.0);
    TTS_ULP_EQUAL(
      erfcx(eve::prev(eve::valmax(as<T>()), 1)), asympt(eve::prev(eve::valmax(as<T>()), 1)), 1.0);
    TTS_ULP_EQUAL(erfcx(eve::valmax(as<T>()) / 2), asympt(eve::valmax(as<T>()) / 2), 1.0);
  }
  TTS_ULP_EQUAL(erfcx(T(1.0E30)), eve::rsqrt(eve::pi(as<T>())) / T(1.0E30), 1.0);
  TTS_ULP_EQUAL(erfcx(-eve::epso_2(as<T>())), T(1.00000000000000012527525318168), 1.0);
  TTS_ULP_EQUAL(erfcx(-T(0.25)), T(1.3586423701047221152100420169489882200138085022721), 1.0);
  TTS_ULP_EQUAL(erfcx(-T(0.5)), T(1.95236048918255709327604771344), 1.0);
  TTS_ULP_EQUAL(erfcx(-T(1)), T(5.00898008076228346630982459821), 1.0);
  TTS_ULP_EQUAL(erfcx(-T(2)), T(108.940904389977972412355433825), 1.0);
  TTS_ULP_EQUAL(erfcx(-T(4)), T(17772220.9040162876484646575921), 1.0);
  TTS_ULP_EQUAL(erfcx(-T(5)), T(144009798674.661040410589634306), 1.0);
  TTS_ULP_EQUAL(erfcx(-T(27)), eve::inf(as<T>()), 0);
  TTS_ULP_EQUAL(erfcx(-T(100)), eve::inf(as<T>()), 0);
  TTS_ULP_EQUAL(erfcx(-eve::valmax(as<T>())), eve::inf(as<T>()), 0);
  if constexpr( eve::platform::supports_invalids )
  {
    TTS_ULP_EQUAL(erfcx(eve::inf(as<T>())), T(0), 0);
    TTS_ULP_EQUAL(erfcx(eve::minf(as<T>())), eve::inf(as<T>()), 0);
    TTS_ULP_EQUAL(erfcx(eve::nan(as<T>())), eve::nan(as<T>()), 0);
  }
};


//==================================================================================================
// Tests for masked erfcx
//==================================================================================================
TTS_CASE_WITH("Check behavior of eve::masked(eve::erfcx)(eve::wide)",
              eve::test::simd::ieee_reals,
              tts::generate(tts::randoms(-5.0, 5.0),
              tts::logicals(0, 3)))
<typename T, typename M>(T const& a0,
                         M const& mask)
{
  TTS_IEEE_EQUAL(eve::erfcx[mask](a0),
            eve::if_else(mask, eve::erfcx(a0), a0));
};
